﻿#if SL5 || WP

// From: http://scrypt.codeplex.com/
// License: http://scrypt.codeplex.com/license

using System;
using System.Collections;

namespace ServiceStack
{
    static internal class Mathematics
    {
        /// <summary>
        /// Bitwise XOR for 2 byte arrays.  Arrays must be the same length.
        /// </summary>
        /// <param name="t1">Left side for comparison</param>
        /// <param name="t2">Right side for comparison</param>
        /// <returns>Resulting byte array</returns>
        static internal byte[] BitwiseXOR(byte[] t1, byte[] t2)
        {
            //Inputs need to be the same length	for this implementation
            if (!(t1.Length == t2.Length))
            {
                throw new ArgumentException("Input arrays must have the same length");
            }

            byte[] result = new byte[t1.Length];
            byte bytXOR = 0;

            for (int i = 0; i <= t1.Length - 1; i++)
            {
                bytXOR = BitwiseXOR(t1[i], t2[i]);

                result[i] = bytXOR;
            }

            return result;
        }

        /// <summary>
        /// Bitwise XOR for 2 Bytes.
        /// </summary>
        /// <param name="t1">Left side for comparison</param>
        /// <param name="t2">Right side for comparison</param>
        /// <returns>Resulting byte</returns>
        static internal byte BitwiseXOR(byte t1, byte t2)
        {
            BitArray baLft = null;
            BitArray baRght = null;
            BitArray baXor = null;

            //Have to use Byte Arrays as the constructor for the BitArray, otherwise the 
            //integer value of the current byte is used to set the length of the bitArray instead of the value.
            byte[] bytL = new byte[1];
            byte[] bytR = new byte[1];

            bytL[0] = t1;
            bytR[0] = t2;
            baLft = new BitArray(bytL);
            baRght = new BitArray(bytR);
            baXor = baLft.Xor(baRght);

            byte[] ba2BytArr = new byte[8];

            baXor.CopyTo(ba2BytArr, 0);

            return ba2BytArr[0];
        }

        /// <summary>
        /// Convert the input Integer to an Octet String.
        /// </summary>
        /// <param name="x">input integer</param>
        /// <param name="size">size in octets (bytes)</param>
        /// <returns>Resulting byte array of specified length</returns>
        static internal byte[] I2OSP(int x, int size)
        {
            byte[] bytVal = BitConverter.GetBytes(x);

            byte[] result = new byte[size];
            Buffer.BlockCopy(bytVal, 0, result, (result.Length - bytVal.Length), bytVal.Length);

            Array.Reverse(result);

            return result;
        }

        /// <summary>
        /// Mask generation function.
        /// </summary>
        /// <param name="seed">Seed</param>
        /// <param name="maskLen">Length of generated mask</param>
        /// <param name="hashLength">Length of the hash produced by the supplied hash provider</param>
        /// <param name="hashProvider">Hash provider to use in mask generation</param>
        /// <returns>Generated mask of specified length</returns>
        static internal byte[] OAEPMGF(byte[] seed, int maskLen, int hashLength, IHashProvider hashProvider)
        {
            byte[] result = new byte[maskLen];

            //Determine how many interations we have to do.  We'll be appending 
            //m_hLen (hash length) bytes for every iteration, so the size of the generated byte array 
            //will be m_hLen * iNum (number of iterations).
            int iNum = (int)Math.Floor(maskLen / hashLength) + 1;

            //Mask that will be truncated to create the final 
            //resulting mask returned by this function.
            byte[] bytLongMask = new byte[(iNum * hashLength)];

            byte[] bytAppend = new byte[4];
            byte[] bytTmp = null;
            int iPadLen = 0;
            byte[] bytSeedHash = new byte[hashLength];

            //Padded pseudorandom seed to be hashed.
            byte[] bytPadSeed = new byte[(seed.Length + 4)];
            seed.CopyTo(bytPadSeed, 0);

            for (int i = 0; i <= iNum - 1; i++)
            {
                //Convert the iterator to an Octet String byte array
                bytTmp = Mathematics.I2OSP(i, 4);

                //Calculate the needed padding zeros, and add 
                //them to the resulting Array.  Result must be 4 bytes long.
                iPadLen = bytAppend.Length - bytTmp.Length;

                bytTmp.CopyTo(bytAppend, 0);

                //Hash the pseudorandom padded seed and append it to the 
                //long version of the mask.
                bytAppend.CopyTo(bytPadSeed, seed.Length);
                bytSeedHash = hashProvider.ComputeHash(bytPadSeed);
                bytSeedHash.CopyTo(bytLongMask, i * hashLength);
            }

            //Copy the first maskLen bytes of bytLongMask to the result
            //and return the result.
            Array.Copy(bytLongMask, result, maskLen);

            return result;

        }
    }

    static internal class DigestEncoding
    {
        static internal byte[] SHA1()
        {
            return new byte[] {
				0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 
				0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
			};

        }

        static internal byte[] SHA256()
        {
            return new byte[] {
				0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
			};

        }

        static internal byte[] SHA384()
        {
            return new byte[] {
				0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30
			};

        }

        static internal byte[] SHA512()
        {
            return new byte[] {
				0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 
				0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40
			};

        }

        static internal byte[] MD2()
        {
            return new byte[] {
				0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 
				0x86, 0xf7, 0x0d, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10
			};

        }

        static internal byte[] MD5()
        {
            return new byte[] {
				0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 
				0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10
			};

        }
    }
}

#endif